package com.book.config;

import com.book.entity.Users;
import com.book.mapper.UserMapper;
import com.book.service.UserAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import java.io.IOException;

/**
 * @Author zzw2000
 * @Date 2022年08月24日 20:10
 * @Description 描述
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Resource
    private UserAuthService userAuthService;
    @Resource
    private PersistentTokenRepository repository;
    @Resource
    private UserMapper userMapper;

    @Bean
    public PersistentTokenRepository jdbcRepository(@Autowired DataSource dataSource) {
        JdbcTokenRepositoryImpl jdbcRepository = new JdbcTokenRepositoryImpl(); //使用基于JDBC的实现
        jdbcRepository.setDataSource(dataSource); //配置数据源
        //jdbcRepository.setCreateTableOnStartup(true); //启动时自动创建用于存储Token的表（建议第一次启动之后删除该行）
        return jdbcRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //首先需要配置哪些请求会被拦截，哪些请求必须具有什么角色才能访问
                .antMatchers("/static/**","/page/auth/**","/api/auth/**").permitAll() //静态资源，使用permitAll来运行任何人访问（注意一定要放在前面）
                //.antMatchers("/**").hasRole("user") //所有请求必须登陆并且是user角色才可以访问（不包含上面的静态资源）
                .antMatchers("/page/user/**").hasRole("user")
                .antMatchers("/page/admin/**").hasRole("admin")
                .anyRequest().hasAnyRole("user", "admin")   //index页面可以由user或admin访问
                //..hasRole("admin")   //除了上面以外的所有内容，只能是admin访问
                .and()
                .formLogin()                    //配置Form表单登陆
                .loginPage("/page/auth/login")            //登陆页面地址（GET）
                .loginProcessingUrl("/api/auth/doLogin") //form表单提交地址（POST）
                .defaultSuccessUrl("/index", true)    //登陆成功后跳转的页面，也可以通过Handler实现高度自定义
                .successHandler(this::onAuthenticationSuccess)
                .permitAll()                    //登陆页面也需要允许所有人访问
                .and()
                .logout()
                .logoutUrl("/api/auth/logout")
                .logoutSuccessUrl("/login")
                .and()
                .csrf().disable()
                .rememberMe() //开启记住我功能
                .rememberMeParameter("remember") //登陆请求表单中需要携带的参数，如果携带，那么本次登陆会被记住
                .tokenRepository(repository)
                .tokenValiditySeconds(60 * 60 * 24 * 7); //Token的有效时间（秒）默认为14天
    }

    private void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) throws IOException {
        HttpSession session = httpServletRequest.getSession();
        Users users = userMapper.getPasswordByUsername(authentication.getName());
        session.setAttribute("user",users);
        if (users.getRole().equals("admin"))
            response.sendRedirect("/page/admin/index");
        else
            response.sendRedirect("/page/user/index");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userAuthService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }
}
